package com.xdan.monitoringservice.listener;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xdan.monitoringservice.config.MyConfig;
import com.xdan.monitoringservice.entity.ResourceEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author:
 * @since: 2023/9/7 13:39
 **/

@Slf4j
@Service
public class ListenerService {


    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();

    @Resource
    private MyConfig myConfig;



    public void listenerInstanceTypes() {
        try {

            String body = HttpRequest.get(myConfig.getTargetUrl())
                    .header("Authorization", myConfig.getApiKey())
                    .execute().body();

            JSONObject jsonObject = JSONUtil.parseObj(body);
            String dataJSON = jsonObject.getStr("data");
//            String body = "1";
//            String dataJSON = "{\"gpu_8x_h100_sxm5\":{\"instance_type\":{\"name\":\"gpu_8x_h100_sxm5\",\"price_cents_per_hour\":2072,\"description\":\"8x H100 (80 GB SXM5)\",\"specs\":{\"vcpus\":208,\"memory_gib\":1800,\"storage_gib\":24780}},\"regions_with_capacity_available\":[]},\"gpu_1x_h100_pcie\":{\"instance_type\":{\"name\":\"gpu_1x_h100_pcie\",\"price_cents_per_hour\":199,\"description\":\"1x H100 (80 GB PCIe)\",\"specs\":{\"vcpus\":26,\"memory_gib\":200,\"storage_gib\":1024}},\"regions_with_capacity_available\":[]},\"gpu_8x_a100_80gb_sxm4\":{\"instance_type\":{\"name\":\"gpu_8x_a100_80gb_sxm4\",\"price_cents_per_hour\":1200,\"description\":\"8x A100 (80 GB SXM4)\",\"specs\":{\"vcpus\":240,\"memory_gib\":1800,\"storage_gib\":20480}},\"regions_with_capacity_available\":[]},\"gpu_1x_a10\":{\"instance_type\":{\"name\":\"gpu_1x_a10\",\"price_cents_per_hour\":60,\"description\":\"1x A10 (24 GB PCIe)\",\"specs\":{\"vcpus\":30,\"memory_gib\":200,\"storage_gib\":1400}},\"regions_with_capacity_available\":[{\"name\":\"us-west-1\",\"description\":\"California, USA\"}]},\"gpu_1x_rtx6000\":{\"instance_type\":{\"name\":\"gpu_1x_rtx6000\",\"price_cents_per_hour\":50,\"description\":\"1x RTX 6000 (24 GB)\",\"specs\":{\"vcpus\":14,\"memory_gib\":46,\"storage_gib\":512}},\"regions_with_capacity_available\":[]},\"gpu_1x_a100\":{\"instance_type\":{\"name\":\"gpu_1x_a100\",\"price_cents_per_hour\":110,\"description\":\"1x A100 (40 GB PCIe)\",\"specs\":{\"vcpus\":30,\"memory_gib\":200,\"storage_gib\":512}},\"regions_with_capacity_available\":[]},\"gpu_1x_a100_sxm4\":{\"instance_type\":{\"name\":\"gpu_1x_a100_sxm4\",\"price_cents_per_hour\":110,\"description\":\"1x A100 (40 GB SXM4)\",\"specs\":{\"vcpus\":30,\"memory_gib\":200,\"storage_gib\":512}},\"regions_with_capacity_available\":[]},\"gpu_2x_a100\":{\"instance_type\":{\"name\":\"gpu_2x_a100\",\"price_cents_per_hour\":220,\"description\":\"2x A100 (40 GB PCIe)\",\"specs\":{\"vcpus\":60,\"memory_gib\":400,\"storage_gib\":1024}},\"regions_with_capacity_available\":[]},\"gpu_4x_a100\":{\"instance_type\":{\"name\":\"gpu_4x_a100\",\"price_cents_per_hour\":440,\"description\":\"4x A100 (40 GB PCIe)\",\"specs\":{\"vcpus\":120,\"memory_gib\":800,\"storage_gib\":1024}},\"regions_with_capacity_available\":[]},\"gpu_8x_a100\":{\"instance_type\":{\"name\":\"gpu_8x_a100\",\"price_cents_per_hour\":880,\"description\":\"8x A100 (40 GB SXM4)\",\"specs\":{\"vcpus\":124,\"memory_gib\":1800,\"storage_gib\":6144}},\"regions_with_capacity_available\":[]},\"gpu_1x_a6000\":{\"instance_type\":{\"name\":\"gpu_1x_a6000\",\"price_cents_per_hour\":80,\"description\":\"1x A6000 (48 GB)\",\"specs\":{\"vcpus\":14,\"memory_gib\":100,\"storage_gib\":200}},\"regions_with_capacity_available\":[]},\"gpu_2x_a6000\":{\"instance_type\":{\"name\":\"gpu_2x_a6000\",\"price_cents_per_hour\":160,\"description\":\"2x A6000 (48 GB)\",\"specs\":{\"vcpus\":28,\"memory_gib\":200,\"storage_gib\":1024}},\"regions_with_capacity_available\":[]},\"gpu_4x_a6000\":{\"instance_type\":{\"name\":\"gpu_4x_a6000\",\"price_cents_per_hour\":320,\"description\":\"4x A6000 (48 GB)\",\"specs\":{\"vcpus\":56,\"memory_gib\":400,\"storage_gib\":1024}},\"regions_with_capacity_available\":[]},\"gpu_8x_v100\":{\"instance_type\":{\"name\":\"gpu_8x_v100\",\"price_cents_per_hour\":440,\"description\":\"8x Tesla V100 (16 GB)\",\"specs\":{\"vcpus\":92,\"memory_gib\":448,\"storage_gib\":6041}},\"regions_with_capacity_available\":[]}}";

            if (StringUtils.isEmpty(dataJSON)) {
                log.error("【没有获取到data结果】{}", body);
                return;
            }

            ArrayList<String> keywords = myConfig.getKeywords();
            ObjectMapper objectMapper = new ObjectMapper();

            JsonNode rootNode = objectMapper.readTree(dataJSON);

            // 遍历JSON数据，查找包含关键词的节点
            Iterator<JsonNode> elements = rootNode.elements();
            while (elements.hasNext()) {
                JsonNode node = elements.next();

                Iterator<JsonNode> regionsWithCapacityAvailable = node.get("regions_with_capacity_available").elements();
                String instanceName = node.get("instance_type").get("name").textValue();

                // 判断有资源, 并且资源在监控列表中
                if (regionsWithCapacityAvailable.hasNext() && keywords.stream().anyMatch(instanceName::equals)) {
                    HashMap<String, String> finalMap = new HashMap<>();
                    JsonNode next = regionsWithCapacityAvailable.next();
                    String regionName = next.get("name").textValue();

                    // 只要us的
                    if (!regionName.contains("us")) {
                        return;
                    }

                    DateTime date = DateUtil.date(System.currentTimeMillis());

                    checkBuyInformationV1(instanceName, regionName);

                    finalMap.put("instanceName", instanceName);
                    finalMap.put("description", next.get("description").textValue());
                    finalMap.put("name", regionName);
                    finalMap.put("date", DateUtil.formatDateTime(date));

                    // 转成json
                    String jsonStr = JSONUtil.toJsonStr(finalMap);

                    // 买过不通知, 没有买过通知
//                    if (buyState) {
                    // 发送消息
                    log.info("【通知资源发送的数据】{}", jsonStr);
                    HttpResponse response = HttpUtil.createPost(myConfig.getNotificationsUrl()).body(jsonStr).execute();
//                    }

                }
            }
            log.info("【获取到的数据】{}", dataJSON);
        } catch (Exception e) {
            log.error("【出现异常】", e);
            throw new RuntimeException(e);
        }
    }


    private boolean checkBuyInformationV1(String instanceName, String regionName) {
        // 判断是否在购买列表 如果在 则继续进行, 如果不在购买列表, 返回true, 发送监控消息
        ArrayList<ResourceEntity> buyList = myConfig.getBuyList();
        boolean listState = false;
        Integer quantityToBePurchased = 0;
        for (ResourceEntity resourceEntity : buyList) {
            if (instanceName.equals(resourceEntity.getName())) {
                listState = true;
                quantityToBePurchased = resourceEntity.getQuantityToBePurchased();
            }
        }

        // 不在购买列表  或者 在购买列表, 但是需要购买的数量小于等于0 直接返回
        if (!listState || quantityToBePurchased <= 0) {
            return true;
        }

        // 检查是否已经到达上限
        if (concurrentHashMap.get(instanceName) != null && concurrentHashMap.get(instanceName) >= quantityToBePurchased) {
            return true;
        }
        // 开启异步线程 购买
        threadPoolTaskExecutor.submit(() -> sendBuyMessage(instanceName, regionName));

        // 没有买过 或者买过, 但是没到购买上限
        int currentQuantity = concurrentHashMap.getOrDefault(instanceName, 0) + 1;
        concurrentHashMap.put(instanceName, currentQuantity);
        return true;
    }

    private void sendBuyMessage(String instanceName, String regionName) {
        String json = myConfig.getBuyTemplate();
        json = json.replace("#{region_name}", regionName);
        json = json.replace("#{instance_type_name}", instanceName);

        log.info("【购买发送的信息】{}", json);

        String body = HttpRequest.post(myConfig.getBuyUrl())
                .header("Authorization", myConfig.getApiKey())
                .body(json)
                .execute().body();
//        String body = "{\"data\":{\"instance_ids\":[\"6df79f22e19d4b25bc2af42ca953fde1\"]}}";
//        String body = "{\"error\":{\"code\":\"instance-operations/launch/insufficient-capacity\",\"message\":\"Not enough capacity to fulfill launch request.\",\"suggestion\":\"Choose an instance type with more availability, or try again later.\"}}";
        log.info("【购买返回的信息】{}", body);

        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = null;
        DateTime date = null;
        try {
            rootNode = objectMapper.readTree(body);

            Iterator<JsonNode> elements = rootNode.elements();
            date = DateUtil.date(System.currentTimeMillis());
            while (elements.hasNext()) {
                JsonNode node = elements.next();
                if (node.toString().contains("instance_ids") && node.get("instance_ids").elements().hasNext()) {
                    String ids = node.get("instance_ids").elements().next().textValue();
                    // 发送消息
                    HashMap<String, String> buySuccessMap = new HashMap<>();
                    buySuccessMap.put("id", ids);
                    buySuccessMap.put("instanceName", instanceName);
                    buySuccessMap.put("regionName", regionName);
                    buySuccessMap.put("date", DateUtil.formatDateTime(date));
                    String jsonStr = JSONUtil.toJsonStr(buySuccessMap);

                    // 发送三次通知, 每次间隔一分钟
                    for (int i = 0; i < 3; i++) {
                        HttpResponse response = HttpUtil.createPost(myConfig.getBuySuccessUrl()).body(jsonStr).execute();
                        try {
                            Thread.sleep(60 * 1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                } else {
                    String code = node.get("code").textValue();
                    String message = node.get("message").textValue();
                    String suggestion = node.get("suggestion").textValue();
                    sendBuyErrorMessage(code, message, suggestion, instanceName, regionName, date);
                    throw new RuntimeException("购买出现异常" + body);
                }
            }
        } catch (Exception e) {
            concurrentHashMap.put(instanceName, concurrentHashMap.get(instanceName) - 1);
            log.error("【购买出现异常】", e);
        }
    }

    private void sendBuyErrorMessage(String code, String message, String suggestion, String instanceName, String regionName, DateTime date) {
        // 发送通知消息
        HashMap<String, String> buyErrorMap = new HashMap<>();
        buyErrorMap.put("instanceName", instanceName);
        buyErrorMap.put("regionName", regionName);
        buyErrorMap.put("date", DateUtil.formatDateTime(date));
        buyErrorMap.put("code", code);
        buyErrorMap.put("message", message);
        buyErrorMap.put("suggestion", suggestion);
        String jsonStr = JSONUtil.toJsonStr(buyErrorMap);
        HttpUtil.createPost(myConfig.getBuyErrorUrl()).body(jsonStr).execute();
    }
}